﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Threading;
using System.Windows.Forms;

using System.Diagnostics;

namespace Nextensions.GUI
{
    [ToolboxItem(true)]
    [DesignTimeVisible(true)]
    [Designer(typeof(NavigationControllerDesigner))]
    public partial class NavigationController : Component
    {
        //public event NotifyDirectionDeadEndHandler DirectionDeadEnd;
        public delegate void NotifyDirectionDeadEndHandler(Directions Direction);
        private delegate void ControlMoveHandler();
        private delegate void CloseHandler();
        private object _sync = new object();
        private bool _cancelPending = false;
        private bool _closeRequested = false;

        public enum Directions
        {
            Back,
            Forward
        }

        private const int ControlPadding = 16;

        private List<Control> _controls = new List<Control>();
        private System.Threading.Timer _slideTimer;
        private int _controlIndex = -1;
        private int _slidingInControlIndex;
        private int _slidingOutControlIndex;
        
        private int[,] _controlIndexAndTargetList;

        public NavigationController()
        {
            InitializeComponent();
        }

        public void RequestClose()
        {
            lock (_sync)
            {
                _cancelPending = true;
                Debug.WriteLine("Set cancelPending to true");
                _closeRequested = true;
            }
        }

        public NavigationController(IContainer container)
        {
            container.Add(this);
            InitializeComponent();
        }

        public void AddControlToStack(Control control)
        {
            control.Location = new Point(ControlPadding, ControlPadding);

            if (_controls.Count == 0)
            {
                _controlIndex = 0;
            }
            else if (_controls.Count == 1)
            {
                if(Orientation == System.Windows.Forms.Orientation.Vertical)
                    control.Top = PageSize + ControlPadding;
                else
                    control.Left = PageSize + ControlPadding;

                control.Visible = false;
            }
            else
            {
                control.Visible = false;
            }

            control.Left = ControlPadding;
            _controls.Add(control);
        }

        public int PageSize
        {
            get;
            set;
        }

        public Form Owner
        {
            get;
            set;
        }

        [DefaultValue(35)]
        public int SlideSpeed
        {
            get;
            set;
        }

        [DefaultValue(Orientation.Horizontal)]
        public Orientation Orientation
        {
            get;
            set;
        }

        public bool Navigate(Directions Direction)
        {
            int step = Direction == Directions.Forward ? 1 : -1;

            _slidingOutControlIndex = _controlIndex;
            _slidingInControlIndex  = _controlIndex + step;

            if (_slidingInControlIndex < 0 || _slidingInControlIndex >= _controls.Count || _slidingOutControlIndex < 0 || _slidingOutControlIndex >= _controls.Count)
            {
                return false;
            }

            int pos;

            if (Orientation == System.Windows.Forms.Orientation.Vertical)
            {
                pos = PageSize;
                _controls[_slidingInControlIndex].Top = PageSize * step;
            }
            else
            {
                pos = PageSize;
                _controls[_slidingInControlIndex].Left = PageSize * step;
            }

            _controls[_slidingInControlIndex].Visible = true;

            //Debug.WriteLine("Sliding in control: " + _slidingInControlIndex);

            _controlIndexAndTargetList = new int[,]
            {
                {_slidingOutControlIndex, ControlPadding - pos * step},
                {_slidingInControlIndex, ControlPadding}
            };

            _slideTimer = new System.Threading.Timer(_slideTimer_Tick, null, 0, SlideSpeed);

            _controlIndex += step;

            return true;
        }

        private void _slideTimer_Tick(object state)
        {
            int sampleControlIndex = _controlIndexAndTargetList[0, 0];
            
            bool destinationReached;

            destinationReached = (Orientation == System.Windows.Forms.Orientation.Vertical) ?
                Math.Abs(_controls[sampleControlIndex].Top - _controlIndexAndTargetList[0, 1]) < 2 :
                Math.Abs(_controls[sampleControlIndex].Left - _controlIndexAndTargetList[0, 1]) < 2;

            if (destinationReached)
            {
                ClearTimer();
                return;
            }

            if (Owner.InvokeRequired)
            {
                if (Owner.IsDisposed || Owner.Disposing)
                {
                    ClearTimer();
                    return;
                }
                try
                {
                    lock (_sync)
                    {
                        Debug.WriteLine("Cancel pending: " + _cancelPending);
                        if (_cancelPending)
                        {
                            ClearTimer();
                            return;
                        }
                    }
                    Debug.WriteLine("Invoking");
                    Owner.
                    Owner.Invoke(new ControlMoveHandler(MoveControls));
                }
                catch
                {
                    if (Owner.IsDisposed || Owner.Disposing)
                    {
                        ClearTimer();
                        return;
                    }
                }
                return;
            }
        }

        private void ClearTimer()
        {
            _slideTimer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
            if (_closeRequested)
            {
                Owner.Invoke(new CloseHandler(CloseOwner));
            }
        }

        private void CloseOwner()
        {
            Owner.Close();
        }

        private void MoveControls()
        {
            int length = _controlIndexAndTargetList.GetLength(0);

            for (int i = 0; i < length; i++)
            {
                //Debug.WriteLine(String.Format("Moving control {0} by: {1}", i, ControlYList[i]));
                int controlIndex = _controlIndexAndTargetList[i,0];
                int pos;

                if (Orientation == System.Windows.Forms.Orientation.Vertical)
                    pos = _controls[controlIndex].Top;
                else
                    pos = _controls[controlIndex].Left;

                float distance = pos - _controlIndexAndTargetList[i, 1];

                if(Orientation == System.Windows.Forms.Orientation.Vertical)
                    _controls[controlIndex].Top = pos - (int)(distance / 5F);
                else
                    _controls[controlIndex].Left = pos - (int)(distance / 5F);
                //_controls[controlIndex].Top += (int)(((float)(_controlIndexAndTargetList[i, 1] - _controls[controlIndex].Top)) / _ease);

                //Debug.WriteLine(String.Format("Control {0} location: {1}", controlIndex, _controls[controlIndex].Location));
            }
           // _controls[_slidingInControlIndex].Top += (ControlPadding - _controls[_slidingInControlIndex].Top) / 4;
            //_controls[_slidingOutControlIndex].Top += (ControlPadding - _controls[_slidingOutControlIndex].Top) / 4;

        }
    }
}
